//
//  HWTouchLabel.m
//  OCDemo
//
//  Created by 李含文 on 2021/4/30.
//

#import "HWTouchLabel.h"

@implementation HWLabelTouchTextModel

@end

@interface HWTouchLabel()

/// 回调block属性
@property (nonatomic, copy) void(^hw_clickBlock)(HWLabelTouchTextModel *item);
/// 数据记录
@property (nonatomic, strong) NSMutableArray *hw_attributeStrings;

/// <#name#>
@property (nonatomic, strong) NSMutableDictionary *hw_effectDic;
/// <#name#>
@property (nonatomic, assign) BOOL hw_isClickAction;
/// <#name#>
@property (nonatomic, assign) BOOL hw_isClickEffect;

@end

@implementation HWTouchLabel

//MARK: 点击效果颜色相关
//- (UIColor *)hw_clickEffectColor {
//    UIColor *obj = objc_getAssociatedObject(self, _cmd);
//    if(obj == nil) {obj = [UIColor clearColor];}
//    return obj;
//}
//- (void)setHw_clickEffectColor:(UIColor *)hw_clickEffectColor {
//    objc_setAssociatedObject(self, @selector(hw_clickEffectColor), hw_clickEffectColor, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
//}
//MARK: 回调block属性
//- (void(^)(HWLabelTouchTextModel *item))hw_clickBlock {
//    return objc_getAssociatedObject(self, _cmd);
//}
//- (void)setHw_clickBlock:(void (^)(HWLabelTouchTextModel * _Nonnull))hw_clickBlock {
//    objc_setAssociatedObject(self, @selector(hw_clickBlock), hw_clickBlock, OBJC_ASSOCIATION_COPY_NONATOMIC);
//}
//MARK: 数据记录
//- (NSMutableArray *)hw_attributeStrings {
//    return objc_getAssociatedObject(self, _cmd);
//}
//- (void)setHw_attributeStrings:(NSMutableArray *)hw_attributeStrings {
//    objc_setAssociatedObject(self, @selector(hw_attributeStrings), hw_attributeStrings, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
//}

//- (NSMutableDictionary *)hw_effectDic {
//    return objc_getAssociatedObject(self, _cmd);
//}
//
//- (void)setHw_effectDic:(NSMutableDictionary *)hw_effectDic {
//    objc_setAssociatedObject(self, @selector(hw_effectDic), hw_effectDic, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
//}
//- (BOOL)hw_isClickAction {
//    return [objc_getAssociatedObject(self, _cmd) boolValue];
//}
//
//- (void)setHw_isClickAction:(BOOL)hw_isClickAction {
//    objc_setAssociatedObject(self, @selector(hw_isClickAction), @(hw_isClickAction), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
//}
//- (BOOL)hw_isClickEffect {
//    return [objc_getAssociatedObject(self, _cmd) boolValue];
//}
//
//- (void)setHw_isClickEffect:(BOOL)hw_isClickEffect {
//    objc_setAssociatedObject(self, @selector(hw_isClickEffect), @(hw_isClickEffect), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
//}
//MARK: - 点击设置
- (void)hw_clickText:(NSArray<NSString *> *)strings action:(void(^)(HWLabelTouchTextModel *item))action {
    self.hw_clickBlock = action;
    [self hw_richTextRangesWithStrings:strings];
}

- (void)hw_richTextRangesWithStrings:(NSArray <NSString *>  *)strings {
    if (self.attributedText == nil) {
        self.hw_isClickAction = NO;
        return;
    }
    self.hw_isClickAction = YES;
    self.hw_isClickEffect = YES;
    self.hw_attributeStrings = [NSMutableArray array];
    __weak typeof(self) weakself = self;
    [strings enumerateObjectsUsingBlock:^(NSString * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        NSString *string = weakself.attributedText.string.copy;
        [weakself hw_screening:string obj:obj index:idx];
        NSLog(@"%ld", self.hw_attributeStrings.count);
    }];
}
//MARK: 筛选
- (void)hw_screening:(NSString *)totalStr obj:(NSString *)obj index:(NSInteger)index{
    NSRange range = [totalStr rangeOfString:obj];
    if (range.length != 0) {
        totalStr = [totalStr stringByReplacingCharactersInRange:range withString:[self hw_getStringWithRange:range]];
        HWLabelTouchTextModel *model = [[HWLabelTouchTextModel alloc] init];
        model.range = range;
        model.content = obj;
        model.index = index;
        model.subIndex = 0;
        if (self.hw_attributeStrings.lastObject) { // 判断是否有上一个元素
            HWLabelTouchTextModel *lastObject = self.hw_attributeStrings.lastObject;
            if (lastObject.index == index) { // 判断最后一个是否是当前角标
                model.subIndex = lastObject.subIndex+1;
            }
        }
        [self.hw_attributeStrings addObject:model];
        [self hw_screening:totalStr obj:obj index:index]; // 递归查找
    }
}
//MARK: 找到后将其替换为空格 避免重复出现
- (NSString *)hw_getStringWithRange:(NSRange)range {
    NSMutableString *string = [NSMutableString string];
    for (int i = 0; i < range.length ; i++) {
        [string appendString:@" "];
    }
    return string;
}


//MARK: - touch
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    if (!self.hw_isClickAction) {
        return;
    }
    UITouch *touch = [touches anyObject];
    CGPoint point = [touch locationInView:self];
    __weak typeof(self) weakself = self;
    [self hw_richTextFrameWithTouchPoint:point result:^(HWLabelTouchTextModel *model) {
        if (weakself.hw_clickBlock) {
            weakself.hw_clickBlock (model);
        }
        if (weakself.hw_isClickEffect) {
            [weakself hw_saveEffectDicWithRange:model.range];
            [weakself hw_clickEffectWithStatus:YES];
        }
    }];
}

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
    if((self.hw_isClickAction) && ([self hw_richTextFrameWithTouchPoint:point result:nil])) {
        return self;
    }
    return [super hitTest:point withEvent:event];
}

#pragma mark - getClickFrame
- (BOOL)hw_richTextFrameWithTouchPoint:(CGPoint)point
                                result:(void(^)(HWLabelTouchTextModel *model))resultBlock {
    CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)self.attributedText);
    CGMutablePathRef Path = CGPathCreateMutable();
    CGPathAddRect(Path, NULL, CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height));
    CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), Path, NULL);
    CFRange range = CTFrameGetVisibleStringRange(frame);
    if (self.attributedText.length > range.length) {
        UIFont *font = nil;
        if ([self.attributedText attribute:NSFontAttributeName atIndex:0 effectiveRange:nil]) {
            font = [self.attributedText attribute:NSFontAttributeName atIndex:0 effectiveRange:nil];
        } else if (self.font){
            font = self.font;
        } else {
            font = [UIFont systemFontOfSize:17];
        }
        CGFloat lineSpace = 0.0f;
        if ([self.attributedText attribute:NSParagraphStyleAttributeName atIndex:0 effectiveRange:nil]) {
            lineSpace = [[[self.attributedText attribute:NSParagraphStyleAttributeName atIndex:0 effectiveRange:nil] valueForKey:@"_lineSpacing"] floatValue];
        }
        CGPathRelease(Path);
        Path = CGPathCreateMutable();
        CGPathAddRect(Path, NULL, CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height + font.lineHeight - lineSpace));
        frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), Path, NULL);
    }
    CFArrayRef lines = CTFrameGetLines(frame);
    if (!lines) {
        if(frame) {CFRelease(frame);}
        if(framesetter) {CFRelease(framesetter);}
        if(Path) {CGPathRelease(Path);}
        return NO;
    }
    CFIndex count = CFArrayGetCount(lines);
    CGPoint origins[count];
    CTFrameGetLineOrigins(frame, CFRangeMake(0, 0), origins);
    CGAffineTransform transform = [self hw_transformForCoreText];
    CGFloat verticalOffset = 0;
    for (CFIndex i = 0; i < count; i++) {
        CGPoint linePoint = origins[i];
        CTLineRef line = CFArrayGetValueAtIndex(lines, i);
        CGRect flippedRect = [self hw_getLineBounds:line point:linePoint];
        CGRect rect = CGRectApplyAffineTransform(flippedRect, transform);
        rect = CGRectInset(rect, 0, 0);
        rect = CGRectOffset(rect, 0, verticalOffset);
        NSParagraphStyle *style = [self.attributedText attribute:NSParagraphStyleAttributeName atIndex:0 effectiveRange:nil];
        CGFloat lineSpace;
        if (style) {
            lineSpace = style.lineSpacing;
        }else {
            lineSpace = 0;
        }
        CGFloat lineOutSpace = (self.bounds.size.height - lineSpace * (count - 1) -rect.size.height * count) / 2;
        rect.origin.y = lineOutSpace + rect.size.height * i + lineSpace * i;
        if (CGRectContainsPoint(rect, point)) {
            CGPoint relativePoint = CGPointMake(point.x - CGRectGetMinX(rect), point.y - CGRectGetMinY(rect));
            CFIndex index = CTLineGetStringIndexForPosition(line, relativePoint);
            CGFloat offset;
            CTLineGetOffsetForStringIndex(line, index, &offset);
            if (offset > relativePoint.x) {
                index = index - 1;
            }
            for (HWLabelTouchTextModel *model in self.hw_attributeStrings) {
                NSRange link_range = model.range;
                if (NSLocationInRange(index, link_range)) {
                    if (resultBlock) {
                        resultBlock(model);
                    }
                    if(frame){CFRelease(frame);}
                    if(framesetter){CFRelease(framesetter);}
                    if(Path){CGPathRelease(Path);}
                    return YES;
                }
            }
        }
    }
    if(frame){CFRelease(frame);}
    if(framesetter){CFRelease(framesetter);}
    if(Path){CGPathRelease(Path);}
    return NO;
}

- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    if (self.hw_isClickEffect) {
        [self performSelectorOnMainThread:@selector(hw_clickEffectWithStatus:) withObject:nil waitUntilDone:NO];
    }
}

- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    if (self.hw_isClickEffect) {
        [self performSelectorOnMainThread:@selector(hw_clickEffectWithStatus:) withObject:nil waitUntilDone:NO];
    }
}

- (CGAffineTransform)hw_transformForCoreText {
    return CGAffineTransformScale(CGAffineTransformMakeTranslation(0, self.bounds.size.height), 1.f, -1.f);
}

- (CGRect)hw_getLineBounds:(CTLineRef)line point:(CGPoint)point {
    CGFloat ascent = 0.0f;
    CGFloat descent = 0.0f;
    CGFloat leading = 0.0f;
    CGFloat width = (CGFloat)CTLineGetTypographicBounds(line, &ascent, &descent, &leading);
    CGFloat height = ascent + fabs(descent) + leading;
    return CGRectMake(point.x, point.y , width, height);
}

#pragma mark - clickEffect
- (void)hw_clickEffectWithStatus:(BOOL)status {
    if (self.hw_isClickEffect) {
        NSMutableAttributedString *attStr = [[NSMutableAttributedString alloc] initWithAttributedString:self.attributedText];
        NSMutableAttributedString *subAtt = [[NSMutableAttributedString alloc] initWithAttributedString:[[self.hw_effectDic allValues] firstObject]];
        NSRange range = NSRangeFromString([[self.hw_effectDic allKeys] firstObject]);
        if (status) {
            [subAtt addAttribute:NSBackgroundColorAttributeName value:self.hw_clickEffectColor range:NSMakeRange(0, subAtt.string.length)];
            [attStr replaceCharactersInRange:range withAttributedString:subAtt];
        } else {
            [attStr replaceCharactersInRange:range withAttributedString:subAtt];
        }
        self.attributedText = attStr;
    }
}

- (void)hw_saveEffectDicWithRange:(NSRange)range {
    self.hw_effectDic = [NSMutableDictionary dictionary];
    NSAttributedString *subAttribute = [self.attributedText attributedSubstringFromRange:range];
    [self.hw_effectDic setObject:subAttribute forKey:NSStringFromRange(range)];
}

@end
